package cn.blnp.net.flowable.boot.localize.dynamic.jump.activity;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.runtime.ChangeActivityStateBuilderImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ExecutionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <h3>童泰跳转与调用子流程结合案例</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/6/18 16:00
 */
@Slf4j
@SpringBootTest
public class DynamicJumpCallFlowTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private IdentityService identityService;
    @Resource
    private TaskService taskService;

    SimplePropertyPreFilter taskFilter = new SimplePropertyPreFilter(Task.class, "id",
            "name", "executionId", "taskDefinitionKey", "assignee", "owner");
    SimplePropertyPreFilter executionFilter = new SimplePropertyPreFilter(Execution.class,
            "id", "parentId", "processInstanceId", "activityId", "isScope");

    /**
     *  <h1>调用子流程的动态跳转案例</h1>
     *  <p>
     *      在流程中包含了1个调用活动，该调用活动引用了一个子流程来构成一个整体流程。
     *      <ul>
     *          <h2>主流程：</h2>
     *          <li><b>流程定义文件：</b>{@link flow/CallActivityDynamicJumpMainProcess.bpmn20.xml}</li>
     *          <li>
     *              <b>流程预览图：</b></br>
     *              <img src="https://cdn4.winhlb.com/2025/06/18/685276ad41454.png" >
     *          </li>
     *          <h2>子流程：</h2>
     *          <li><b>流程定义文件：</b>{@link flow/CallActivityDynamicJumpSubProcess.bpmn20.xml}</li>
     *          <li>
     *              <b>流程预览图：</b></br>
     *              <img src="https://cdn4.winhlb.com/2025/06/18/685276740e366.png" >
     *          </li>
     *      </ul>
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("flow/CallActivityDynamicJumpMainProcess.bpmn20.xml")
                .addClasspathResource("flow/CallActivityDynamicJumpSubProcess.bpmn20.xml")
                .name("调用式子流程的动态跳转案例")
                .deploy();
        log.info("部署ID：{}", deploy.getId());
        //流程定义信息
        List<ProcessDefinition> definitionList = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .list();
        ProcessDefinition parentProcessDefinition = definitionList.stream()
                .filter(pd -> pd.getKey().equals("CallActivityDynamicJumpMainProcess"))
                .findFirst().orElse(null);
        ProcessDefinition subProcessDefinition = definitionList.stream()
                .filter(pd -> pd.getKey().equals("CallActivityDynamicJumpSubProcess"))
                .findFirst().orElse(null);
        log.info("(主流程)定义ID：{}", parentProcessDefinition.getId());
        log.info("(子流程)定义ID：{}", subProcessDefinition.getId());

        //启动流程实例
        ProcessInstance procInst = runtimeService
                .startProcessInstanceById(parentProcessDefinition.getId());
        log.info("流程实例ID：{}", procInst.getId());
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery()
                .rootProcessInstanceId(procInst.getId());
        Task task1 = getTaskQuery(executionQuery).singleResult();
        log.info("第一次跳转前，当前任务为：{}", JSON.toJSONString(task1, taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        /**
         *  特别留意：
         *  1、在使用该案例功能时，若未启用实体链接服务配置。则执行第一次跳转时会抛出以下异常：
         *  2025-06-18 17:14:27.370 ERROR 55532 --- [           main] o.f.c.e.impl.interceptor.CommandContext  : Error while closing command context
         *
         * java.lang.NullPointerException: null
         * 	at org.flowable.engine.impl.util.EntityLinkUtil.createEntityLinks(EntityLinkUtil.java:41) ~[flowable-engine-6.8.0.jar:6.8.0]
         *
         * 	启用方法如下所示：
         * 	{@code
         * 	    setEnableEntityLinks(true)
         * 	}
         **/
        //从主流程的UserTask1跳转到子流程的UserTask5
        getChangeActivityStateBuilder()
                .processInstanceId(procInst.getId())
                .moveActivityIdToSubProcessInstanceActivityId("task1", "task5",
                        "callActivity1")
                .changeState();
        log.info("第一次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从子流程的UserTask5跳转到主流程的UserTask4
        getChangeActivityStateBuilder()
                .processInstanceId(getSubProcessInstanceId(procInst.getId()))
                .moveActivityIdToParentActivityId("task5", "task4")
                .changeState();
        log.info("第二次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从主流程的UserTask4跳转到子流程的UserTask7、UserTask8
        getChangeActivityStateBuilder()
                .moveSingleActivityIdToSubProcessInstanceActivityIds("task4",
                        Arrays.asList("task7", "task8"), "callActivity1",
                        subProcessDefinition.getVersion())
                .processInstanceId(procInst.getId())
                .changeState();
        log.info("第三次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从子流程的UserTask7、UserTask8跳转到主流程的UserTask1
        getChangeActivityStateBuilder()
                .moveActivityIdsToParentActivityId(Arrays.asList("task7", "task8"),
                        "task1", "妲己", "")
                .processInstanceId(getSubProcessInstanceId(procInst.getId()))
                .changeState();
        log.info("第四次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从主流程的UserTask1跳转到主流程的UserTask2、UserTask3
        getChangeActivityStateBuilder()
                .processInstanceId(procInst.getId())
                .moveSingleActivityIdToActivityIds("task1", Arrays.asList("task2", "task3"))
                .changeState();
        log.info("第五次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从主流程的UserTask2、UserTask3跳转到子流程的UserTask10
        getChangeActivityStateBuilder()
                .moveActivityIdsToSubProcessInstanceActivityId(Arrays.asList("task2", "task3"),
                        "task10", "callActivity1",
                        subProcessDefinition.getVersion(), "大乔", "")
                .processInstanceId(procInst.getId())
                .changeState();
        log.info("第六次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        //从子流程的UserTask10跳转到主流程的UserTask2、UserTask3
        getChangeActivityStateBuilder()
                .moveSingleActivityIdToParentActivityIds("task10",
                        Arrays.asList("task2", "task3"))
                .processInstanceId(getSubProcessInstanceId(procInst.getId()))
                .changeState();
        log.info("第七次跳转后，当前任务为：{}", JSON.toJSONString(getTaskQuery(executionQuery).list(),
                taskFilter));
        log.info("当前执行实例有：{}", JSON.toJSONString(executionQuery.list(), executionFilter));

        /**
         *  演示结果：
         *  2025-06-18 17:18:17.249  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 部署ID：235001
         * 2025-06-18 17:18:17.257  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : (主流程)定义ID：CallActivityDynamicJumpMainProcess:10:235006
         * 2025-06-18 17:18:17.257  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : (子流程)定义ID：CallActivityDynamicJumpSubProcess:10:235007
         * 2025-06-18 17:18:17.582  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 流程实例ID：235008
         * 2025-06-18 17:18:17.922  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第一次跳转前，当前任务为：{"executionId":"235009","id":"235013","name":"UserTask1","taskDefinitionKey":"task1"}
         * 2025-06-18 17:18:17.942  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"task1","id":"235009","isScope":false,"parentId":"235008","processInstanceId":"235008"}]
         * 2025-06-18 17:18:18.015  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第一次跳转后，当前任务为：[{"executionId":"235019","id":"235021","name":"UserTask5","taskDefinitionKey":"task5"}]
         * 2025-06-18 17:18:18.019  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"callActivity1","id":"235015","isScope":false,"parentId":"235008","processInstanceId":"235008"},{"id":"235017","isScope":true,"processInstanceId":"235017"},{"activityId":"task5","id":"235019","isScope":false,"parentId":"235017","processInstanceId":"235017"}]
         * 2025-06-18 17:18:18.237  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第二次跳转后，当前任务为：[{"executionId":"235024","id":"235026","name":"UserTask4","taskDefinitionKey":"task4"}]
         * 2025-06-18 17:18:18.241  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"task4","id":"235024","isScope":false,"parentId":"235008","processInstanceId":"235008"}]
         * 2025-06-18 17:18:18.315  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第三次跳转后，当前任务为：[{"executionId":"235032","id":"235035","name":"UserTask7","taskDefinitionKey":"task7"},{"executionId":"235033","id":"235039","name":"UserTask8","taskDefinitionKey":"task8"}]
         * 2025-06-18 17:18:18.323  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"callActivity1","id":"235028","isScope":false,"parentId":"235008","processInstanceId":"235008"},{"id":"235030","isScope":true,"processInstanceId":"235030"},{"activityId":"task7","id":"235032","isScope":false,"parentId":"235030","processInstanceId":"235030"},{"activityId":"task8","id":"235033","isScope":false,"parentId":"235030","processInstanceId":"235030"}]
         * 2025-06-18 17:18:18.457  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第四次跳转后，当前任务为：[{"assignee":"妲己","executionId":"235042","id":"235044","name":"UserTask1","taskDefinitionKey":"task1"}]
         * 2025-06-18 17:18:18.462  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"task1","id":"235042","isScope":false,"parentId":"235008","processInstanceId":"235008"}]
         * 2025-06-18 17:18:18.520  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第五次跳转后，当前任务为：[{"executionId":"235048","id":"235051","name":"UserTask2","taskDefinitionKey":"task2"},{"executionId":"235049","id":"235054","name":"UserTask3","taskDefinitionKey":"task3"}]
         * 2025-06-18 17:18:18.527  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"task2","id":"235048","isScope":false,"parentId":"235008","processInstanceId":"235008"},{"activityId":"task3","id":"235049","isScope":false,"parentId":"235008","processInstanceId":"235008"}]
         * 2025-06-18 17:18:18.617  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第六次跳转后，当前任务为：[{"executionId":"235060","id":"235062","name":"UserTask10","taskDefinitionKey":"task10"}]
         * 2025-06-18 17:18:18.622  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"callActivity1","id":"235056","isScope":false,"parentId":"235008","processInstanceId":"235008"},{"id":"235058","isScope":true,"processInstanceId":"235058"},{"activityId":"task10","id":"235060","isScope":false,"parentId":"235058","processInstanceId":"235058"}]
         * 2025-06-18 17:18:18.731  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 第七次跳转后，当前任务为：[{"executionId":"235065","id":"235068","name":"UserTask2","taskDefinitionKey":"task2"},{"executionId":"235066","id":"235071","name":"UserTask3","taskDefinitionKey":"task3"}]
         * 2025-06-18 17:18:18.744  INFO 68952 --- [           main] .b.n.f.b.l.d.j.a.DynamicJumpCallFlowTest : 当前执行实例有：[{"id":"235008","isScope":true,"processInstanceId":"235008"},{"activityId":"task2","id":"235065","isScope":false,"parentId":"235008","processInstanceId":"235008"},{"activityId":"task3","id":"235066","isScope":false,"parentId":"235008","processInstanceId":"235008"}]
         **/
    }

    /**
     * <p><b>用途:查询主子流程的所有用户任务的查询对象<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 16:29 2025/6/18
     * @params [executionQuery] 
     * @param executionQuery 
     * @return org.flowable.task.api.TaskQuery
    **/
    private TaskQuery getTaskQuery(ExecutionQuery executionQuery) {
        List<Execution> list = executionQuery.list();
        List<String> processIds = list.stream()
                .map(Execution::getProcessInstanceId)
                .distinct().collect(Collectors.toList());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceIdIn(processIds);
        return taskQuery;
    }

    /**
     * <p><b>用途:获取调用活动所引用的流程实例编号<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 16:29 2025/6/18
     * @params [parentProcInstId] 
     * @param parentProcInstId 
     * @return java.lang.String
    **/
    private String getSubProcessInstanceId(String parentProcInstId) {
        ProcessInstance subProcInst = runtimeService
                .createProcessInstanceQuery()
                .superProcessInstanceId(parentProcInstId)
                .singleResult();
        return subProcInst.getId();
    }

    /**
     * <p><b>用途:获取ChangeActivityStateBuilder<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 16:30 2025/6/18
     * @params [] 
     * @param  
     * @return org.flowable.engine.impl.runtime.ChangeActivityStateBuilderImpl
    **/
    private ChangeActivityStateBuilderImpl getChangeActivityStateBuilder() {
        return ((ChangeActivityStateBuilderImpl) runtimeService
                .createChangeActivityStateBuilder());
    }
}
